﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Windows.Forms;
using RaGE_JD.Properties;

namespace RaGE_JD
{
    public partial class MainWindow
    {
        private void ClearParams()
        {
            grpProcParams.Text = Resources.ProcedureParameters;
            lblParam1.Text = Resources.NULL;
            lblParam2.Text = Resources.NULL;
            lblParam3.Text = Resources.NULL;
            lblParam4.Text = Resources.NULL;
            lblParam5.Text = Resources.NULL;
            lblParam6.Text = Resources.NULL;
            lblParam7.Text = Resources.NULL;
            lblParam1.Text = Resources.NULL;
            txtParam1.Text = "";
            txtParam2.Text = "";
            txtParam3.Text = "";
            txtParam4.Text = "";
            txtParam5.Text = "";
            txtParam6.Text = "";
            txtParam7.Text = "";
            txtParamTarget.Text = "";
        }

        private void InitAIPolicy()
        {
            comboProcedure.Items.Clear();

            comboProcedure.Items.AddRange(new object[]
            {
                "Attack",
                "Cast_Skill",
                "Broadcast_Message",
                "Reset_Aggro",
                "Execute_ActionSet",
                "Disable_ActionSet",
                "Enable_ActionSet",
                "Create_Timer",
                "Delete_Timer",
                "Flee",
                "Be_Taunted",
                "Fade_Target",
                "Fade_Aggro",
                "Break",
                "NPC_Generator",
                "Summon_Mob",
                "Unknown16",
                "Set_Path",
                "Disappear",
                "N/A",
                "N/A",
                "Respawn",
                "Set_Value",
                "Add_Value",
                "N/A",
                "Set_Mob_Attribute",
                "Drop_Warsoul"
            });

            ProcedureList();
        }

        private object[] ReadParameters(int type, BinaryReader br, int ver)
        {
            switch (type)
            {
                // Type 0 - Attack(int unk)
                case 0:
                    return new object[] { br.ReadInt32() };

                // Type 1 - Cast_Skill(int skill_id, int skill_lvl)
                case 1:
                    return new object[] { br.ReadInt32(), br.ReadInt32() };

                // Type 2 - Broadcast_Message(int byteCount, byte[] message)
                case 2:
                    var byteCount = br.ReadInt32();
                    return new object[] { byteCount, br.ReadBytes(byteCount) };

                // Type 3 - Reset_Aggro()
                case 3:
                    return new object[0];

                // Type 4 - Exec_ActionSet(int actionset_id)
                case 4:
                    return new object[] { br.ReadInt32() };

                // Type 5 - Disable_ActionSet(int actionset_id)
                case 5:
                    return new object[] { br.ReadInt32() };

                // Type 6 - Enable_ActionSet(int actionset_id)
                case 6:
                    return new object[] { br.ReadInt32() };

                // Type 7 - Create_Timer(int timerID, int delay, int cycles)
                case 7:
                    return new object[] { br.ReadInt32(), br.ReadInt32(), br.ReadInt32() };

                // Type 8 - Delete_Timer(int timerID)
                case 8:
                    return new object[] { br.ReadInt32() };

                // Type 9 - Flee()
                case 9:
                    return new object[0];

                // Type 10 - Be_Taunted()
                case 10:
                    return new object[0];

                // Type 11 - Unknown11()
                case 11:
                    return new object[0];

                // Type 12 - Fade_Aggro()
                case 12:
                    return new object[0];

                // Type 13 - Unknown13()
                case 13:
                    return new object[0];

                // Type 14 - Trigger(int triggerID, int ctrlParam)
                case 14:
                    return new object[] { br.ReadInt32(), br.ReadInt32() };

                // Type 15 - Summon_Mob(int elementsID, int timeInterval, int calls, int Survival, string mobName, int refreshRange, int unknown)
                case 15:
                    return new object[] { br.ReadInt32(), br.ReadInt32(), br.ReadInt32(), br.ReadInt32(), br.ReadBytes(32), br.ReadInt32(), br.ReadInt32() };

                // Type 16 - Unknown16()
                case 16:
                    return new object[] { br.ReadInt32() };

                // Type 17 - Set_Path(int pathID, int v11) for version 11 else Set_Path(int pathID)
                case 17:
                    if (ver == 11) return new object[] { br.ReadInt32(), br.ReadInt32() };
                    if (ver < 11) return new object[] { br.ReadInt32() };
                    break;

                // Type 18 - Disappear()
                case 18:
                    return new object[0];

                // // Type 19 - N/A
                //case 19:

                // Type 20 - N/A
                //case 20:

                // Type 21 - Respawn()
                case 21:
                    return new object[0];

                // Type 22 - Set_Value(int oldValue, int newValue)
                case 22:
                    return new object[] { br.ReadInt32(), br.ReadInt32() };

                // Type 23 - Add_Value(int value, int toAdd)
                case 23:
                    return new object[] { br.ReadInt32(), br.ReadInt32() };

                // Type 24 - N/A
                //case 24:

                // Type 25 - Set_Mob_Attribute(int mobID, int cycles, int unk1, int name, int unk2)
                case 25:
                    return new object[] { br.ReadInt32(), br.ReadInt32(), br.ReadInt32(), br.ReadInt32(), br.ReadInt32() };

                // Type 26 - Drop_WarSoul(int itemID, int calls, int cycles)
                case 26:
                    return new object[] { br.ReadInt32(), br.ReadInt32(), br.ReadInt32() };
            }

            Cursor = Cursors.Default;
            MessageBox.Show(Resources.UnknownType + type.ToString(CultureInfo.InvariantCulture) + Resources.CRLF);
            return new object[0];
        }

        private string ConditionName(int operID)
        {
            switch (operID)
            {
                case 0:
                    return Resources.Is_Timer_Ticking;
                case 1:
                    return Resources.Is_HP_Less;
                case 2:
                    return Resources.Is_Combat_Started;
                case 3:
                    return Resources.Randomize;
                case 4:
                    return Resources.Is_Target_Killed;
                case 5:
                    return "!";
                case 6:
                    return "||";
                case 7:
                    return "&&";
                case 8:
                    return Resources.Is_Dead;
                case 9:
                    return Resources.Path_To;
                case 10:
                    return Resources.More_Than;
                case 11:
                    return Resources.Distance_To;
                case 12:
                    return Resources.Unknown12;
                case 13:
                    return Resources.Unknown13;
                case 14:
                    return Resources.Unknown14;
                case 15:
                    return Resources.Unknown15;
                case 16:
                    return ">";
                case 17:
                    return "<";
                case 18:
                    return "=";
                case 19:
                    return Resources.Public_Counter;
                case 20:
                    return Resources.Value;
                case 21:
                    return Resources.Rank;
                case 22:
                    return Resources.Remove_Mob;
                case 23:
                    return Resources.Skill_Cast;
                default:
                    {
                        Cursor = Cursors.Default;
                        MessageBox.Show(Resources.UnkCondType + operID.ToString(CultureInfo.InvariantCulture));
                        return "?";
                    }
            }
        }

        private string ConditionValue(Condition c)
        {
            switch (c.OperID)
            {
                case 0:    // Is_Timer_Ticking[0]
                case 9:    // Path_To[0]
                case 10:   // More_Than[0]
                case 19:   // Variable[0]
                case 20:   // Variable_Value[0]
                case 21:   // Rank[0]
                case 23:   // Cast_Skill[0]
                    return BitConverter.ToInt32(c.Value, 0).ToString(CultureInfo.InvariantCulture);

                case 1:    // Is_HP_Less[0.00]
                case 3:    // Randomize[0.00]
                case 11:   // Distance_To[0.00]
                    return BitConverter.ToSingle(c.Value, 0).ToString("F2");

                case 2:    // Is_Combat_Started[]
                case 4:    // Is_Target_Killed[]
                case 6:    // ||
                case 7:    // &&
                case 8:    // Is_Dead[]
                case 12:   // Unknown12[]
                case 13:   // Unknown13[]
                case 14:   // Unknown14[]
                case 15:   // Unknown15[]
                case 16:   // >
                case 17:   // <
                case 18:   // =
                case 22:   // Remove_Mob[]
                    return "";

                case 5:    // !
                    return "NOT";
                default:
                    {
                        Cursor = Cursors.Default;
                        var operID = c.OperID;
                        MessageBox.Show(Resources.UnkCondType + operID.ToString(CultureInfo.InvariantCulture));
                        return "?";
                    }
            }
        }

        private string ConditionExpression(Condition c)
        {
            var text = "";

            if (c.ConditionType == 1)
            {
                text += "(";
                text += ConditionExpression(c.ConditionLeft);
                text = text + " " + ConditionName(c.OperID) + " ";
                text += ConditionExpression(c.ConditionRight);
                text += ")";
            }

            if (c.ConditionType == 2)
            {
                text += ConditionName(c.OperID);
                text += "(";
                text += ConditionExpression(c.ConditionRight);
                text += ")";
            }

            if (c.ConditionType > 2)
            {
                text += ConditionName(c.OperID);
                text += "[";
                if (c.ArgBytes > 0) text += ConditionValue(c);
                text += "]";
            }

            return text;
        }

        private static string ProcedureTarget(int target)
        {
            switch (target)
            {
                case 0:
                    return Resources.AGGRO_FIRST;
                case 1:
                    return Resources.AGGRO_SECOND;
                case 2:
                    return Resources.AGGRO_SECOND_RAND;
                case 3:
                    return Resources.MOST_HP;
                case 4:
                    return Resources.MOST_MP;
                case 5:
                    return Resources.LEAST_HP;
                case 6:
                    return Resources.CLASS_COMBO;
                case 7:
                    return Resources.SELF;
                default:
                    return "?";
            }
        }

        private static int ProcedureTarget(string target)
        {
            if (target.ToString(CultureInfo.InvariantCulture) == Resources.AGGRO_FIRST) return 0;
            if (target.ToString(CultureInfo.InvariantCulture) == Resources.AGGRO_SECOND) return 1;
            if (target.ToString(CultureInfo.InvariantCulture) == Resources.AGGRO_SECOND_RAND) return 2;
            if (target.ToString(CultureInfo.InvariantCulture) == Resources.MOST_HP) return 3;
            if (target.ToString(CultureInfo.InvariantCulture) == Resources.MOST_MP) return 4;
            if (target.ToString(CultureInfo.InvariantCulture) == Resources.LEAST_HP) return 5;
            if (target.ToString(CultureInfo.InvariantCulture) == Resources.CLASS_COMBO) return 6;
            return target.ToString(CultureInfo.InvariantCulture) == Resources.SELF ? 7 : 0;
        }

        private static string ProcedureExpression(Procedure p, int ver)
        {
            var str = "";

            switch (p.Type)
            {
                case 0:
                    str = "Attack(";
                    str += ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture);
                    str += ")";
                    break;

                case 1:
                    str = "Cast_Skill(";
                    str = str + ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str += ((int)p.Parameter[1]).ToString(CultureInfo.InvariantCulture);
                    str += ")";
                    break;

                case 2:
                    str = "Broadcast_Message(";
                    str = str + ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str = str + "\"" + Encoding.Unicode.GetString((byte[])p.Parameter[1]).Replace("\0", "") + "\"";
                    str += ")";
                    break;

                case 3:
                    str = "Reset_Aggro()";
                    break;

                case 4:
                    str = "Execute_ActionSet(";
                    str += ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture);
                    str += ")";
                    break;

                case 5:
                    str = "Disable_ActionSet(";
                    str += ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture);
                    str += ")";
                    break;

                case 6:
                    str = "Enable_ActionSet(";
                    str += ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture);
                    str += ")";
                    break;

                case 7:
                    str = "Create_Timer(";
                    str = str + ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str = str + ((int)p.Parameter[1]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str += ((int)p.Parameter[2]).ToString(CultureInfo.InvariantCulture);
                    str += ")";
                    break;

                case 8:
                    str = "Delete_Timer(";
                    str += ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture);
                    str += ")";
                    break;

                case 9:
                    str = "Flee()";
                    break;

                case 10:
                    str = "Be_Taunted()";
                    break;

                case 11:
                    str = "Fade_Target()";
                    break;

                case 12:
                    str = "Fade_Aggro()";
                    break;

                case 13:
                    str = "Break()";
                    break;

                case 14:
                    var paramStr = (int)p.Parameter[1] % 2 < 1 ? "start" : "stop";
                    str = "NPC_Generator(";
                    str += ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str += ((int)p.Parameter[1]).ToString(CultureInfo.InvariantCulture) + "[" + paramStr + "]";
                    str += ")";
                    break;

                case 15:
                    str = "Summon_Mob(";
                    str = str + ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str = str + ((int)p.Parameter[1]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str = str + ((int)p.Parameter[2]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str = str + ((int)p.Parameter[3]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str = str + "\"" + Encoding.Unicode.GetString((byte[])p.Parameter[4]).Replace("\0", "") + "\",";
                    str = str + ((int)p.Parameter[5]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str += ((int)p.Parameter[6]).ToString(CultureInfo.InvariantCulture);
                    str += ")";
                    break;

                case 16:
                    str = "Unknown16(";
                    str = str + ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture);
                    str += ")";
                    break;

                case 17:
                    str = "Set_Path(";
                    str += ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture);
                    if (ver == 11) str = str + ", " + ((int)p.Parameter[1]).ToString(CultureInfo.InvariantCulture);
                    str += ")";
                    break;

                case 18:
                    str = "Disappear()";
                    break;

                case 21:
                    str = "Respawn()";
                    break;

                case 22:
                    str = "Set_Value(";
                    str = str + ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str += ((int)p.Parameter[1]).ToString(CultureInfo.InvariantCulture);
                    str += ")";
                    break;

                case 23:
                    str = "Add_Value(";
                    str = str + ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str += ((int)p.Parameter[1]).ToString(CultureInfo.InvariantCulture);
                    str += ")";
                    break;

                case 24:
                    break;

                case 25:
                    str = "Set_Mob_Attribute(";
                    str = str + ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str = str + ((int)p.Parameter[1]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str = str + ((int)p.Parameter[2]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str = str + ((int)p.Parameter[3]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str += ((int)p.Parameter[4]).ToString(CultureInfo.InvariantCulture);
                    str += ")";
                    break;

                case 26:
                    str = "Drop_WarSoul(";
                    str = str + ((int)p.Parameter[0]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str = str + ((int)p.Parameter[1]).ToString(CultureInfo.InvariantCulture) + ", ";
                    str += ((int)p.Parameter[2]).ToString(CultureInfo.InvariantCulture);
                    str += ")";
                    break;
            }

            str = str + " Target(" + ProcedureTarget(p.Target);

            if (p.Target == 6)
            {
                var extra = p.TargetParams;
                str = str + ", " + extra;
            }

            return str + ")";
        }

        private object[] GetParameters(int type, int ver)
        {
            // Type 0 - Attack(int unk)
            if (type == 0 && txtParam1.Text != "")
                return new object[] { Convert.ToInt32(txtParam1.Text) };

            // Type 1 - Cast_Skill(int skill_id, int skill_lvl)
            if (type == 1 && txtParam1.Text != "" && txtParam2.Text != "")
                return new object[]
                {
                    Convert.ToInt32(txtParam1.Text),
                    Convert.ToInt32(txtParam2.Text)
                };

            // Type 2 - Broadcast_Message(int byteCount, byte[] message)
            if (type == 2 && txtParam2.Text != "")
            {
                var unicode = Encoding.Unicode;
                var bytes = unicode.GetBytes(txtParam2.Text);
                var byteLen = bytes.Length + 2;
                var message = new byte[byteLen];

                Array.Copy(bytes, message, bytes.Length);
                message[bytes.Length] = 0;
                message[bytes.Length + 1] = 0;
                return new object[] { byteLen, message };
            }

            // Type 3 - Reset_Aggro()
            if (type == 3) return new object[0];

            // Type 4 - Execute_ActionSet(int actionset_id)
            if (type == 4 && txtParam1.Text != "")
                return new object[] { Convert.ToInt32(txtParam1.Text) };

            // Type 5 - Disable_ActionSet(int actionset_id)
            if (type == 5 && txtParam1.Text != "")
                return new object[] { Convert.ToInt32(txtParam1.Text) };

            // Type 6 - Enable_ActionSet(int actionset_id)
            if (type == 6 && txtParam1.Text != "")
                return new object[] { Convert.ToInt32(txtParam1.Text) };

            // Type 7 - Create_Timer(int timerID, int delay, int cycles)
            if (type == 7 && txtParam1.Text != "" && txtParam2.Text != "" && txtParam3.Text != "")
                return new object[]
                {
                    Convert.ToInt32(txtParam1.Text),
                    Convert.ToInt32(txtParam2.Text),
                    Convert.ToInt32(txtParam3.Text)
                };

            // Type 8 - Delete_Timer(int timerID)
            if (type == 8 && txtParam1.Text != "")
                return new object[] { Convert.ToInt32(txtParam1.Text) };

            // Type 9 - Flee()
            if (type == 9) return new object[0];

            // Type 10 - Be_Taunted()
            if (type == 10) return new object[0];

            // Type 11 - Fade_Target()
            if (type == 11) return new object[0];

            // Type 12 - Fade_Aggro()
            if (type == 12) return new object[0];

            // Type 13 - Break()
            if (type == 13) return new object[0];

            // Type 14 - NPC_Generator(int triggerID, int ctrlParam)
            if (type == 14 && txtParam1.Text != "" && txtParam2.Text != "")
                return
                    new object[]
                    {
                        Convert.ToInt32(txtParam1.Text),
                        Convert.ToInt32(txtParam2.Text)
                    };

            // Type 15 - Summon_Mob(int elementsID, int timeInterval, int calls, int Survival, string mobName, int refreshRange, int unknown)
            if (type == 15 && txtParam1.Text != "" && txtParam2.Text != "" &&
                txtParam3.Text != "" && txtParam4.Text != "" && txtParam5.Text != "" &&
                txtParam6.Text != "" && txtParam7.Text != "")
            {
                var mobName = new byte[32];
                var msgSize = Encoding.Unicode.GetBytes(txtParam5.Text);

                if (msgSize.Length < 32)
                {
                    Array.Copy(msgSize, mobName, msgSize.Length);
                    for (var i = msgSize.Length; i < 32; i++) mobName[i] = 0;
                }
                else Array.Copy(msgSize, mobName, 32);

                return new object[]
                {
                    Convert.ToInt32(txtParam1.Text),
                    Convert.ToInt32(txtParam2.Text),
                    Convert.ToInt32(txtParam3.Text),
                    Convert.ToInt32(txtParam4.Text),
                    mobName,
                    Convert.ToInt32(txtParam6.Text),
                    Convert.ToInt32(txtParam7.Text)
                };
            }

            // Type 16 - Unknown16(int unkInt string unkStr)
            if (type == 16 && txtParam1.Text != "")
                return new object[] { Convert.ToInt32(txtParam1.Text) };

            // Type 17 - Set_Path(int pathID)
            //      if version = 11 then Set_Path(int pathID, int v11Path)
            if (type == 17 && txtParam1.Text != "")
            {
                return ver == 11 ? new object[] {Convert.ToInt32(txtParam1.Text),
                    Convert.ToInt32(txtParam2.Text)} : new object[] { Convert.ToInt32(txtParam1.Text) };
            }

            // Type 18 - Disappear()
            if (type == 18) return new object[0];

            // Type 19 - N/A

            // Type 20 - N/A

            // Type 21 - Respawn()
            if (type == 21) return new object[0];

            // Type 22 - Set_Value(int oldValue, int newValue)
            if (type == 22 && txtParam1.Text != "" && txtParam2.Text != "")
                return new object[]
                {
                    Convert.ToInt32(txtParam1.Text),
                    Convert.ToInt32(txtParam2.Text)
                };

            // Type 23 - Add_Value(int value, int toAdd)
            if (type == 23 && txtParam1.Text != "" && txtParam2.Text != "")
                return new object[]
                {
                    Convert.ToInt32(txtParam1.Text),
                    Convert.ToInt32(txtParam2.Text)
                };

            // Type 24 - N/A

            // Type 25 - Set_Mob_Attribute(int mobID, int cycles, int unk1, int name, int unk2)
            if (type == 25 && txtParam1.Text != "" && txtParam2.Text != "" &&
                txtParam3.Text != "" && txtParam4.Text != "" && txtParam5.Text != "")
                return new object[]
                {
                    Convert.ToInt32(txtParam1.Text),
                    Convert.ToInt32(txtParam2.Text),
                    Convert.ToInt32(txtParam3.Text),
                    Convert.ToInt32(txtParam4.Text),
                    Convert.ToInt32(txtParam5.Text)
                };

            // Type 26 - Drop_WarSoul(int itemID, int calls, int cycles)
            if (type == 26 && txtParam1.Text != "" && txtParam2.Text != "" && txtParam3.Text != "")
                return new object[]
                {
                    Convert.ToInt32(txtParam1.Text),
                    Convert.ToInt32(txtParam2.Text),
                    Convert.ToInt32(txtParam3.Text)
                };

            // Type Error
            Cursor = Cursors.Default;
            MessageBox.Show(Resources.ErrParamsWrong);
            return null;
        }

        private static Condition GetCondition(string s)
        {
            var pos = 1;
            var len = 0;
            var str = s.Substring(1);

            if (s[0] == '(')
            {
                var ndx = 1;

                while (ndx <= str.Length && pos >= 0)
                {
                    if (s[ndx] == ')')
                    {
                        pos--;
                        len = ndx;
                    }

                    if (s[ndx] == '(') pos++;
                    ndx++;
                }

                if (len == 0 || pos != 0) return null;

                if (len == str.Length)
                {
                    s = str.Substring(0, len - 1);
                    return GetCondition(s);
                }
            }

            var length = 0;
            var stack1 = new Stack();
            var stack2 = new Stack();
            var text = s.Substring(length, 1);

            while (text != "" || stack1.Count != 0)
            {
                if (AIPolicy.IsNumber(text))
                {
                    text = AIPolicy.GetNumber(s.Substring(length));
                    stack2.Push(text);
                    length += text.Length;
                }
                else
                {
                    string s2;
                    if (text == "(")
                    {
                        if (stack1.Count != 0)
                        {
                            length--;
                            s2 = s.Substring(0, length);
                            str = s.Substring(length + 1);

                            var condition = new Condition { OperID = AIPolicy.IDOper(stack1.Pop().ToString()) };
                            condition.ConditionType = AIPolicy.GetOperPrime(condition.OperID);
                            condition.ArgBytes = AIPolicy.GetOperArgBytes(condition.OperID);
                            condition.Value = new byte[condition.ArgBytes];

                            if (condition.ConditionType == 1)
                            {
                                condition.ConditionLeft = GetCondition(s2);
                                condition.SubNodeL = AIPolicy.GetSubNodeL(condition.OperID);
                                condition.ConditionRight = GetCondition(str);
                                condition.SubNodeR = AIPolicy.GetSubNodeR(condition.OperID);
                            }

                            if (condition.ConditionType == 2)
                            {
                                condition.ConditionRight = GetCondition(str);
                                condition.SubNodeL = AIPolicy.GetSubNodeL(condition.OperID);
                            }

                            return condition;
                        }
                        length = len + 1;
                    }
                    else
                    {
                        if (stack1.Count != 0 && (text == "" || AIPolicy.GetOperPrime(AIPolicy.IDOper(stack1.Peek().ToString())) <=
                            AIPolicy.GetOperPrime(AIPolicy.IDOper(text))))
                        {
                            length--;
                            s2 = s.Substring(0, length);
                            str = s.Substring(length + 1);

                            var condition = new Condition { OperID = AIPolicy.IDOper(stack1.Pop().ToString()) };
                            condition.ConditionType = AIPolicy.GetOperPrime(condition.OperID);
                            condition.ArgBytes = AIPolicy.GetOperArgBytes(condition.OperID);

                            var value = new byte[condition.ArgBytes];

                            switch (condition.OperID)
                            {
                                case 0:
                                case 9:
                                case 10:
                                case 19:
                                case 20:
                                case 21:
                                case 23:
                                    value = BitConverter.GetBytes(Convert.ToInt32(stack2.Pop().ToString()));
                                    break;

                                case 1:
                                case 3:
                                case 11:
                                    value = BitConverter.GetBytes(Convert.ToSingle(stack2.Pop().ToString()));
                                    break;
                            }

                            condition.Value = value;

                            if (condition.ConditionType == 1)
                            {
                                condition.ConditionLeft = GetCondition(s2);
                                condition.SubNodeL = AIPolicy.GetSubNodeL(condition.OperID);
                                condition.ConditionRight = GetCondition(str);
                                condition.SubNodeR = AIPolicy.GetSubNodeR(condition.OperID);
                            }

                            if (condition.ConditionType == 2)
                            {
                                condition.ConditionRight = GetCondition(str);
                                condition.SubNodeL = AIPolicy.GetSubNodeL(condition.OperID);
                            }

                            return condition;
                        }

                        stack1.Push(text);
                        length++;
                    }
                }

                text = length < s.Length ? s.Substring(length, 1) : "";
            }

            return null;
        }

        private Condition FixCondition(Condition c)
        {
            if (c.OperID == 22) return AI.ActionController[441].ActionSet[0].Condition;
            if (c.OperID > 23) return null;

            if (c.ConditionType == 1)
            {
                c.ConditionLeft = FixCondition(c.ConditionLeft);
                c.ConditionRight = FixCondition(c.ConditionRight);

                if (c.ConditionLeft != null && c.ConditionRight != null) return c;
                if (c.ConditionLeft == null && c.ConditionRight != null) return c.ConditionRight;
                if (c.ConditionLeft != null && c.ConditionRight == null) return c.ConditionLeft;
                if (c.ConditionLeft == null && c.ConditionRight == null) return null;
            }

            if (c.ConditionType == 2)
            {
                c.ConditionRight = FixCondition(c.ConditionRight);
                return c.ConditionRight != null ? c : null;
            }

            return c;
        }

        private void ProcedureList()
        {
            switch (comboProcedure.SelectedIndex)
            {
                // Attack(int unk)
                case 0:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Attack;
                    lblParam1.Text = Resources.AttackParam;
                    break;

                // Cast_Skill(int skill_id, int skill_lvl)
                case 1:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Cast_Skill;
                    lblParam1.Text = Resources.SkillID;
                    lblParam2.Text = Resources.SkillLevel;
                    break;

                // Broadcast_Message(int byteCount, byte[] message)
                case 2:
                    btnAIMsgAsst.Visible = true;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Broadcast_Message;
                    lblParam1.Text = Resources.ByteCount;
                    lblParam2.Text = Resources.Message;
                    break;

                // Reset_Aggro()
                case 3:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Reset_Aggro;
                    break;

                // Execute_ActionSet(int actionset_id)
                case 4:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Execute_ActionSet;
                    lblParam1.Text = Resources.ActionSetID;
                    break;

                // Disable_ActionSet(int actionset_id)
                case 5:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Disable_ActionSet;
                    lblParam1.Text = Resources.ActionSetID;
                    break;

                // Enable_ActionSet(int actionset_id)
                case 6: btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Enable_ActionSet;
                    lblParam1.Text = Resources.ActionSetID;
                    break;

                // Create_Timer(int timerID, int delay, int cycles)
                case 7:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Create_Timer;
                    lblParam1.Text = Resources.TimerID;
                    lblParam2.Text = Resources.Delay;
                    lblParam3.Text = Resources.Cycles;
                    break;

                // Delete_Timer(int timerID)
                case 8:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Delete_Timer;
                    lblParam1.Text = Resources.TimerID;
                    break;

                // Flee()
                case 9:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Flee;
                    break;

                // Be_Taunted()
                case 10:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Be_Taunted;
                    break;

                // Fade_Target()
                case 11:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Fade_Target;
                    break;

                // Fade_Aggro()
                case 12:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Fade_Aggro;
                    break;

                // Break()
                case 13:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Break;
                    break;

                // NPC_Generator(int triggerID, int ctrlParam)
                case 14:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.NPC_Generator;
                    lblParam1.Text = Resources.TriggerID;
                    lblParam2.Text = Resources.Control;
                    break;

                // Summon_Mob(int elementsID, int timeInterval, int calls, int Survival, string mobName, int refreshRange, int unknown)
                case 15:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Summon_Mob;
                    lblParam1.Text = Resources.ElementsID;
                    lblParam2.Text = Resources.TimeInterval;
                    lblParam3.Text = Resources.Calls;
                    lblParam4.Text = Resources.Survival;
                    lblParam5.Text = Resources.MobName;
                    lblParam6.Text = Resources.RefreshRange;
                    lblParam7.Text = Resources.Unknown;
                    break;

                // Unknown16(int unknown)
                case 16:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Unknown16;
                    lblParam1.Text = Resources.Unknown;
                    break;

                // Set_Path(int pathID)
                // if version == 11 then Set_Path(int pathID, int v11Path)
                case 17:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Set_Path;
                    lblParam1.Text = Resources.PathID;
                    lblParam2.Text = Resources.v11Path;
                    break;

                // Disappear()
                case 18:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Disappear;
                    break;

                // N/A
                case 19:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.NA;
                    break;

                // N/A
                case 20:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.NA;
                    break;

                // Respawn()
                case 21:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Respawn;
                    break;

                // Set_Value(int Value, int Amount)
                case 22:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Set_Value;
                    lblParam1.Text = Resources.Value;
                    lblParam2.Text = Resources.EQUAL;
                    break;

                // Add_Value(int Value, int Amount)
                case 23:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Add_Value;
                    lblParam1.Text = Resources.Value;
                    lblParam2.Text = Resources.PLUS;
                    break;

                // N/A
                case 24:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.NA;
                    break;

                // Set_Mob_Attribute(int mobID, int cycles, int unk1, int name, int unk2)
                case 25:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Set_Mob_Attribute;
                    lblParam1.Text = Resources.MobID;
                    lblParam2.Text = Resources.Cycles;
                    lblParam3.Text = Resources.Unknown;
                    lblParam4.Text = Resources.MobName;
                    lblParam5.Text = Resources.Unknown;
                    break;

                // Drop_WarSoul(int itemID, int calls, int cycles)
                case 26:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.AddProcedure + Resources.Drop_WarSoul;
                    lblParam1.Text = Resources.ItemID;
                    lblParam2.Text = Resources.Calls;
                    lblParam3.Text = Resources.Cycles;
                    break;
            }
        }

        private static void SaveCondition(Condition c, BinaryWriter bw)
        {
            bw.Write(c.OperID);
            bw.Write(c.ArgBytes);
            bw.Write(c.Value);
            bw.Write(c.ConditionType);

            if (c.ConditionType == 1)
            {
                SaveCondition(c.ConditionLeft, bw);
                bw.Write(c.SubNodeL);

                if (c.SubNodeL == 2)
                {
                    SaveCondition(c.ConditionRight, bw);
                    bw.Write(c.SubNodeR);
                }
            }

            if (c.ConditionType != 2) return;

            SaveCondition(c.ConditionRight, bw);
            bw.Write(c.SubNodeL);
        }

        private static void WriteParameters(int type, IList<object> parameters, BinaryWriter bw, int ver)
        {
            switch (type)
            {
                // Type 0 - Attack(int unk)
                case 0:
                    bw.Write((int)parameters[0]);
                    break;

                // Type 1 - Cast_Skill(int skill_id, int skill_lvl)
                case 1:
                    bw.Write((int)parameters[0]);
                    bw.Write((int)parameters[1]);
                    break;

                // Type 2 - Broadcast_Message(int byteCount, byte[] message)
                case 2:
                    var value = (int)parameters[0];
                    bw.Write(value);
                    bw.Write((byte[])parameters[1]);
                    break;

                // Type 3 - Reset_Aggro()
                // no parameters...
                case 3:
                    break;

                // Type 4 - Execute_ActionSet(int actionset_id)
                case 4:
                    bw.Write((int)parameters[0]);
                    break;

                // Type 5 - Disable_ActionSet(int actionset_id)
                case 5:
                    bw.Write((int)parameters[0]);
                    break;

                // Type 6 - Enable_ActionSet(int actionset_id)
                case 6:
                    bw.Write((int)parameters[0]);
                    break;

                // Type 7 - Create_Timer(int timerID, int delay, int cycles)
                case 7:
                    bw.Write((int)parameters[0]);
                    bw.Write((int)parameters[1]);
                    bw.Write((int)parameters[2]);
                    break;

                // Type 8 - Delete_Timer(int timerID)
                case 8:
                    bw.Write((int)parameters[0]);
                    break;

                // Type 9 - Flee()
                // no parameters...
                case 9:

                // Type 10 - Be_Taunted()
                // no parameters...
                case 10:

                // Type 11 - Fade_Target()
                // no parameters...
                case 11:

                // Type 12 - Fade_Aggro()
                // no parameters...
                case 12:

                // Tpye 13 - Break()
                // no parameters...
                case 13:
                    break;

                // Type 14 - NPC_Generator(int triggerID, int ctrlParam)
                case 14:
                    bw.Write((int)parameters[0]);
                    bw.Write((int)parameters[1]);
                    break;

                // Type 15 - Summon_Mob(int elementsID, int timeInterval, int calls, int Survival, string mobName, int refreshRange, int unknown)
                case 15:
                    bw.Write((int)parameters[0]);
                    bw.Write((int)parameters[1]);
                    bw.Write((int)parameters[2]);
                    bw.Write((int)parameters[3]);
                    bw.Write((byte[])parameters[4]);
                    bw.Write((int)parameters[5]);
                    bw.Write((int)parameters[6]);
                    break;

                // Type 16 - Unknown16(int unk)
                case 16:
                    bw.Write((int)parameters[0]);
                    break;

                // Type 17 - Set_Path(int pathID)
                // if version = 11 then Set_Path(int pathID, int v11Path)
                case 17:
                    bw.Write((int)parameters[0]);
                    if (ver == 11) bw.Write((int)parameters[1]);
                    break;

                // Type 18 - Disappear()
                case 18:

                // Type 19 - N/A
                case 19:

                // Type 20 - N/A
                case 20:

                // Type 21 - Respawn()
                case 21:
                    break;

                // Type 22- Set_Value(int oldValue, int newValue)
                case 22:
                    bw.Write((int)parameters[0]);
                    bw.Write((int)parameters[1]);
                    break;

                // Type 23- Add_Value(int value, int toAdd)
                case 23:
                    bw.Write((int)parameters[0]);
                    bw.Write((int)parameters[1]);
                    break;

                // Type 24-  N/A
                case 24:
                    break;

                // Type 25 - Set_Mob_Attribute(int mobID, int cycles, int unk1, int name, int unk2)
                case 25:
                    bw.Write((int)parameters[0]);
                    bw.Write((int)parameters[1]);
                    bw.Write((int)parameters[2]);
                    bw.Write((int)parameters[3]);
                    bw.Write((int)parameters[4]);
                    break;

                // Type 26 - Drop_WarSoul(int itemID, int calls, int cycles)
                case 26:
                    bw.Write((int)parameters[0]);
                    bw.Write((int)parameters[1]);
                    bw.Write((int)parameters[2]);
                    break;
            }
        }

        private void OpenAIPolicy()
        {
            var openAIPolicy = new OpenFileDialog();
            openAIPolicy.Filter = Resources.AIPolicyFilter;
            openAIPolicy.Title = Resources.SelectAIPolicy;
            openAIPolicy.FileName = "aipolicy.data";

            if (openAIPolicy.ShowDialog() != DialogResult.OK || !File.Exists(openAIPolicy.FileName)) return;

            try
            {
                Cursor = Cursors.WaitCursor;
                listController.Items.Clear();
                listActionSet.Items.Clear();
                txtCondition.Clear();
                listProcedure.Items.Clear();
                progressBar.Style = ProgressBarStyle.Blocks;

                var fileStream = File.OpenRead(openAIPolicy.FileName);
                var binaryReader = new BinaryReader(fileStream);
                AI = new AIPolicy();

                AI.Signature = binaryReader.ReadInt32();
                AI.ActionControllerCount = binaryReader.ReadInt32();
                AI.ActionController = new ActionController[AI.ActionControllerCount];

                for (var i = 0; i < AI.ActionController.Length; i++)
                {
                    AI.ActionController[i] = new ActionController();

                    AI.ActionController[i].Signature = binaryReader.ReadInt32();
                    AI.ActionController[i].ID = binaryReader.ReadInt32();
                    AI.ActionController[i].ActionSetsCount = binaryReader.ReadInt32();

                    AI.ActionController[i].ActionSet = new ActionSet[AI.ActionController[i].ActionSetsCount];
                    listController.Items.Add(AI.ActionController[i].ID.ToString(CultureInfo.InvariantCulture));

                    for (var j = 0; j < AI.ActionController[i].ActionSet.Length; j++)
                    {
                        AI.ActionController[i].ActionSet[j] = new ActionSet();

                        AI.ActionController[i].ActionSet[j].Version = binaryReader.ReadInt32();
                        var version = AI.ActionController[i].ActionSet[j].Version;

                        AI.ActionController[i].ActionSet[j].ID = binaryReader.ReadInt32();
                        AI.ActionController[i].ActionSet[j].Flags = binaryReader.ReadBytes(3);
                        AI.ActionController[i].ActionSet[j].ActionSetName = binaryReader.ReadBytes(128);

                        AI.ActionController[i].ActionSet[j].Condition = Condition.LoadCondition(binaryReader);
                        AI.ActionController[i].ActionSet[j].ProcedureCount = binaryReader.ReadInt32();
                        AI.ActionController[i].ActionSet[j].Procedure =
                            new Procedure[AI.ActionController[i].ActionSet[j].ProcedureCount];

                        for (var k = 0; k < AI.ActionController[i].ActionSet[j].Procedure.Length; ++k)
                        {
                            AI.ActionController[i].ActionSet[j].Procedure[k] =
                                new Procedure { Type = binaryReader.ReadInt32() };

                            var procedure = AI.ActionController[i].ActionSet[j].Procedure[k];
                            var objArray = ReadParameters(AI.ActionController[i].ActionSet[j].Procedure[k].Type, binaryReader, version);

                            procedure.Parameter = objArray;
                            AI.ActionController[i].ActionSet[j].Procedure[k].Target = binaryReader.ReadInt32();
                        }
                    }
                }
                binaryReader.Close();
                fileStream.Dispose();

                Cursor = Cursors.Default;
                progressBar.Style = ProgressBarStyle.Continuous;
                openAIPolicy.Dispose();
            }
            catch (Exception aipEx)
            {
                Cursor = Cursors.Default;
                MessageBox.Show(Resources.ErrLoadAIPolicy + aipEx.Message);
            }
        }

        private void BtnExprCalcClick(object sender, EventArgs e)
        {
            (new ExprCalc()).Show(this);
        }

        private void UpdateController(object sender, EventArgs e)
        {
            ClearParams();
            btnAIMsgAsst.Visible = false;

            if (AI == null || listController.SelectedIndex <= -1) return;

            var cSelectedIndex = listController.SelectedIndex;
            var actionSet = AI.ActionController[cSelectedIndex].ActionSet;

            listActionSet.Items.Clear();
            txtCtrlID.Text = listController.SelectedItem.ToString();

            foreach (var aSet in actionSet)
            {
                var iD = aSet.ID;
                listActionSet.Items.Add("[" + iD.ToString(CultureInfo.InvariantCulture) + "] " + aSet.Name);
                txtCtrlID.Text = AI.ActionController[cSelectedIndex].ID.ToString(CultureInfo.InvariantCulture);
            }

            txtActionID.Clear();
            txtFlag1.Clear();
            txtFlag2.Clear();
            txtFlag3.Clear();
            txtActionName.Clear();
            txtCondition.Clear();
            listProcedure.Items.Clear();
        }

        private void UpdateActionSet(object sender, EventArgs e)
        {
            ClearParams();
            btnAIMsgAsst.Visible = false;

            if (AI == null || listController.SelectedIndex <= -1 || listActionSet.SelectedIndex <= -1) return;

            var cSelectedIndex = listController.SelectedIndex;
            var asSelectedIndex = listActionSet.SelectedIndex;

            txtCondition.Text = ConditionExpression(AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Condition);

            listProcedure.Items.Clear();

            for (var i = 0; i < AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure.Length; i++)
            {
                listProcedure.Items.Add("[" + i.ToString(CultureInfo.InvariantCulture) + "] " +
                    ProcedureExpression(AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i],
                    AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Version));

                txtActionID.Text = AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].ID.ToString(CultureInfo.InvariantCulture);
                txtFlag1.Text = AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Flags[0].ToString(CultureInfo.InvariantCulture);
                txtFlag2.Text = AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Flags[1].ToString(CultureInfo.InvariantCulture);
                txtFlag3.Text = AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Flags[2].ToString(CultureInfo.InvariantCulture);
                txtActionName.Text = AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Name.ToString(CultureInfo.InvariantCulture);
            }
        }

        private void UpdateProcedure(object sender, EventArgs e)
        {
            grpProcParams.Text = Resources.ProcedureParameters;
            ClearParams();

            if (AI == null || listController.SelectedIndex <= -1 || listActionSet.SelectedIndex <= -1 ||
                listProcedure.SelectedIndex <= -1) return;

            var cSelectedIndex = listController.SelectedIndex;
            var asSelectedIndex = listActionSet.SelectedIndex;
            var pSelectedIndex = listProcedure.SelectedIndex;

            int timerID;
            int delay;
            int cycles;
            int unk1;
            int actionID;
            int value;
            int amt;

            switch (AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Type)
            {
                // Type 0 - Attack(int unk)
                case 0:
                    grpProcParams.Text = Resources.Attack;
                    lblParam1.Text = Resources.AttackParam;

                    var param1 = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = param1.ToString(CultureInfo.InvariantCulture);
                    break;

                // Cast_Skill(int skill_id, int skill_lvl)
                case 1:
                    grpProcParams.Text = Resources.Cast_Skill;
                    lblParam1.Text = Resources.SkillID;
                    lblParam2.Text = Resources.SkillLevel;
                    btnAIMsgAsst.Visible = false;

                    var skillID = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = skillID.ToString(CultureInfo.InvariantCulture);

                    var skillLvl = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[1];
                    txtParam2.Text = skillLvl.ToString(CultureInfo.InvariantCulture);
                    break;

                // Broadcast_Message(int byteCount, byte[] message)
                case 2:
                    grpProcParams.Text = Resources.Broadcast_Message;
                    lblParam1.Text = Resources.ByteCount;
                    lblParam2.Text = Resources.Message;
                    btnAIMsgAsst.Visible = true;

                    var byteCount = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = byteCount.ToString(CultureInfo.InvariantCulture);
                    txtParam2.Text = Encoding.Unicode.GetString((byte[])AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].
                        Procedure[pSelectedIndex].Parameter[1]);
                    break;

                // Reset_Aggro()
                case 3:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.Reset_Aggro;
                    break;

                // Execute_ActionSet(int ActionSetID)
                case 4:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.Execute_ActionSet;
                    lblParam1.Text = Resources.ActionSetID;

                    actionID = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = actionID.ToString(CultureInfo.InvariantCulture);
                    break;

                // Disable_ActionSet(int ActionSetID)
                case 5:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.Disable_ActionSet;
                    lblParam1.Text = Resources.ActionSetID;
                    actionID = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = actionID.ToString(CultureInfo.InvariantCulture);
                    break;

                // Enable_ActionSet(int ActionSetID)
                case 6:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.Enable_ActionSet;
                    lblParam1.Text = Resources.ActionSetID;
                    actionID = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = actionID.ToString(CultureInfo.InvariantCulture);
                    break;

                // Create_Timer(int TimerID, int Delay, int Cycles)
                case 7:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.Create_Timer;
                    lblParam1.Text = Resources.TimerID;
                    lblParam2.Text = Resources.Delay;
                    lblParam3.Text = Resources.Cycles;

                    timerID = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = timerID.ToString(CultureInfo.InvariantCulture);

                    delay = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[1];
                    txtParam2.Text = delay.ToString(CultureInfo.InvariantCulture);

                    cycles = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[2];
                    txtParam3.Text = cycles.ToString(CultureInfo.InvariantCulture);
                    break;

                // Delete_Timer(int TimerID)
                case 8:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.Delete_Timer;
                    lblParam1.Text = Resources.TimerID;

                    var num11 = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = num11.ToString(CultureInfo.InvariantCulture);
                    break;

                // Flee()
                case 9:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.Flee;
                    break;

                // Be_Taunted
                case 10:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.Be_Taunted;
                    break;

                // Fade_Target()
                case 11:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.Fade_Target;
                    break;

                // Fade_Aggro()
                case 12:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.Fade_Aggro;
                    break;

                // Break()
                case 13:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.Break;
                    break;

                // NPC_Generator(int TriggerID, int CtrlParam)
                case 14:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.NPC_Generator;

                    lblParam1.Text = Resources.TriggerID;
                    lblParam2.Text = Resources.Control;

                    var triggerID = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = triggerID.ToString(CultureInfo.InvariantCulture);

                    var ctrlParam = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[1];
                    txtParam2.Text = ctrlParam.ToString(CultureInfo.InvariantCulture);
                    break;

                // Summon_Mob(int ElementsID, int TimeInterval, int Calls, int Survival, string MobName, int RefreshRange, int Unknown)
                case 15:
                    btnAIMsgAsst.Visible = false;

                    grpProcParams.Text = Resources.Summon_Mob;
                    lblParam1.Text = Resources.ElementsID;
                    lblParam2.Text = Resources.TimeInterval;
                    lblParam3.Text = Resources.Calls;
                    lblParam5.Text = Resources.Survival;
                    lblParam5.Text = Resources.MobName;
                    lblParam6.Text = Resources.RefreshRange;
                    lblParam7.Text = Resources.Unknown;

                    var elementsID = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = elementsID.ToString(CultureInfo.InvariantCulture);

                    var timeInt = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[1];
                    txtParam2.Text = timeInt.ToString(CultureInfo.InvariantCulture);

                    var calls = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[2];
                    txtParam3.Text = calls.ToString(CultureInfo.InvariantCulture);

                    var survival = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[3];
                    txtParam4.Text = survival.ToString(CultureInfo.InvariantCulture);

                    txtParam5.Text = Encoding.Unicode.GetString((byte[])AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].
                        Procedure[pSelectedIndex].Parameter[4]);

                    var refreshRange = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[5];
                    txtParam6.Text = refreshRange.ToString(CultureInfo.InvariantCulture);

                    var unknown = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[6];
                    txtParam7.Text = unknown.ToString(CultureInfo.InvariantCulture);

                    break;

                // Unknown16(int Unknown)
                case 16:
                    btnAIMsgAsst.Visible = false;

                    grpProcParams.Text = Resources.Unknown16;
                    lblParam1.Text = Resources.Unknown;

                    unk1 = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = unk1.ToString(CultureInfo.InvariantCulture);

                    break;

                // Set_Path(int PathID)
                // if (version == 11) Set_Path(int PathID, int v11Path)
                case 17:
                    btnAIMsgAsst.Visible = false;

                    grpProcParams.Text = Resources.Set_Path;
                    lblParam1.Text = Resources.PathID;

                    var pathID = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = pathID.ToString(CultureInfo.InvariantCulture);

                    if (AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Version == 11)
                    {
                        lblParam2.Text = Resources.v11Path;
                        var v11Path = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[1];
                        txtParam2.Text = v11Path.ToString(CultureInfo.InvariantCulture);
                    }

                    break;

                // Disappear()
                case 18:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.Disappear;
                    break;

                // N/A
                case 19:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.NA;
                    break;

                // N/A
                case 20:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.NA;
                    break;

                // Respawn()
                case 21:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.Respawn;
                    break;

                // Set_Value(int Value, int amt)
                case 22:
                    btnAIMsgAsst.Visible = false;

                    grpProcParams.Text = Resources.Set_Value;
                    lblParam1.Text = Resources.Value;
                    lblParam2.Text = Resources.EQUAL;

                    value = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = value.ToString(CultureInfo.InvariantCulture);

                    amt = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[1];
                    txtParam2.Text = amt.ToString(CultureInfo.InvariantCulture);
                    break;

                // Add_Value(int value, int amt)
                case 23:
                    btnAIMsgAsst.Visible = false;

                    grpProcParams.Text = Resources.Add_Value;
                    lblParam1.Text = Resources.Value;
                    lblParam2.Text = Resources.PLUS;

                    value = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = value.ToString(CultureInfo.InvariantCulture);

                    amt = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[1];
                    txtParam2.Text = amt.ToString(CultureInfo.InvariantCulture);
                    break;

                // N/A
                case 24:
                    btnAIMsgAsst.Visible = false;
                    grpProcParams.Text = Resources.NA;
                    break;

                // Set_Mob_Attribute(int mobID, int cycles, int unk1, int name, int unk2)
                case 25:
                    btnAIMsgAsst.Visible = false;

                    grpProcParams.Text = Resources.Set_Mob_Attribute;
                    lblParam1.Text = Resources.MobID;
                    lblParam2.Text = Resources.Cycles;
                    lblParam3.Text = Resources.Unknown;
                    lblParam5.Text = Resources.MobID;
                    lblParam5.Text = Resources.Unknown;

                    var mobID = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = mobID.ToString(CultureInfo.InvariantCulture);

                    cycles = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[1];
                    txtParam2.Text = cycles.ToString(CultureInfo.InvariantCulture);

                    unk1 = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[2];
                    txtParam3.Text = unk1.ToString(CultureInfo.InvariantCulture);

                    var name = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[3];
                    txtParam4.Text = name.ToString(CultureInfo.InvariantCulture);

                    var unk2 = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[4];
                    txtParam5.Text = unk2.ToString(CultureInfo.InvariantCulture);
                    break;

                // Drop_WarSoul(int itemID, int calls, int cycles)
                case 26:
                    btnAIMsgAsst.Visible = false;

                    grpProcParams.Text = Resources.Drop_WarSoul;
                    lblParam1.Text = Resources.ItemID;
                    lblParam2.Text = Resources.Calls;
                    lblParam3.Text = Resources.Cycles;

                    timerID = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[0];
                    txtParam1.Text = timerID.ToString(CultureInfo.InvariantCulture);

                    delay = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[1];
                    txtParam2.Text = delay.ToString(CultureInfo.InvariantCulture);

                    cycles = (int)AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter[2];
                    txtParam3.Text = cycles.ToString(CultureInfo.InvariantCulture);
                    break;
            }
            // Type Target
            var target = AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Target;
            txtParamTarget.Text = ProcedureTarget(target);
        }

        internal void UpdateTarget(string target)
        {
            txtParamTarget.Text = target;
        }

        private void BtnAITargetSelClick(object sender, EventArgs e)
        {
            (new TargetSelector()).Show(this);
        }

        internal void UpdateMsg(string txtLen, string msg)
        {
            txtParam1.Text = txtLen;
            txtParam2.Text = msg;
        }

        private void BtnAIMsgAsstClick(object sender, EventArgs e)
        {
            (new MsgAsst()).Show(this);
        }

        internal void UpdateExpr(string expr)
        {
            txtCondition.Text = expr;
        }

        private void SaveAIPolicy()
        {
            var saveAIPolicy = new SaveFileDialog();
            saveAIPolicy.InitialDirectory = Environment.CurrentDirectory;
            saveAIPolicy.Filter = Resources.AIPolicyFilter;
            if (AI == null || saveAIPolicy.ShowDialog() != DialogResult.OK || saveAIPolicy.FileName == "") return;

            Cursor = Cursors.WaitCursor;
            progressBar.Style = ProgressBarStyle.Blocks;

            var aipStream = new FileStream(saveAIPolicy.FileName, FileMode.Create, FileAccess.Write);
            var aipWriter = new BinaryWriter(aipStream);

            aipWriter.Write(AI.Signature);
            aipWriter.Write(AI.ActionControllerCount);

            foreach (var actionController in AI.ActionController)
            {
                aipWriter.Write(actionController.Signature);
                aipWriter.Write(actionController.ID);
                aipWriter.Write(actionController.ActionSetsCount);

                foreach (var actionSet in actionController.ActionSet)
                {
                    var version = actionSet.Version;

                    aipWriter.Write(actionSet.Version);
                    aipWriter.Write(actionSet.ID);
                    aipWriter.Write(actionSet.Flags);
                    aipWriter.Write(actionSet.ActionSetName);

                    SaveCondition(actionSet.Condition, aipWriter);

                    aipWriter.Write(actionSet.ProcedureCount);

                    foreach (var procedure in actionSet.Procedure)
                    {
                        aipWriter.Write(procedure.Type);
                        WriteParameters(procedure.Type, procedure.Parameter, aipWriter, version);
                        aipWriter.Write(procedure.Target);
                    }
                }
            }

            aipWriter.Close();
            aipStream.Close();
            saveAIPolicy.Dispose();

            progressBar.Style = ProgressBarStyle.Continuous;
            Cursor = Cursors.Default;
            MessageBox.Show(Resources.FileSaved);
        }

        private void BtnEditConditionClick(object sender, EventArgs e)
        {
            if (AI == null)
            {
                MessageBox.Show(Resources.OpenAIPolicyFirst + Resources.EditAnExpression);
                return;
            }

            if (listActionSet.SelectedIndex < 0) return;

            if (txtCondition.Text == "")
            {
                MessageBox.Show(Resources.NoExpressionEntered);
                return;
            }

            var cSelectedIndex = listController.SelectedIndex;
            var asSelectedIndex = listActionSet.SelectedIndex;
            var text = txtCondition.Text;

            text = text.Replace(" ", "");
            text = text.Replace(Resources.Is_Timer_Ticking, "a");  // Is_Timer_Ticking[0]
            text = text.Replace(Resources.Is_HP_Less, "b");        // Is_HP_Less[0.00]
            text = text.Replace(Resources.Is_Combat_Started, "c"); // Is_Combat_Started[]
            text = text.Replace(Resources.Randomize, "d");         // Randomize[0.00]
            text = text.Replace(Resources.Is_Target_Killed, "e");  // Is_Target_Killed[]
            text = text.Replace("||", "f");                        // OR
            text = text.Replace("&&", "g");                        // AND
            text = text.Replace(Resources.Is_Dead, "h");           // Is_Dead[]
            text = text.Replace(Resources.Path_To, "i");           // Path_To[0]
            text = text.Replace(Resources.More_Than, "j");         // More_Than[0]
            text = text.Replace(Resources.Distance_To, "k");       // Distance_To[0.00]
            text = text.Replace(Resources.Unknown12, "l");         // Unknown12[]
            text = text.Replace(Resources.Unknown13, "m");         // Unknown13[]
            text = text.Replace(Resources.Unknown14, "n");         // Unknown14[]
            text = text.Replace(Resources.Unknown15, "o");         // Unknown15[]
            text = text.Replace(Resources.Public_Counter, "q");    // Public_Counter[0]
            text = text.Replace(Resources.Value, "p");             // Value[0]
            text = text.Replace(Resources.Rank, "r");              // Rank[0]
            text = text.Replace(Resources.Remove_Mob, "s");        // Remove_Mob[]
            text = text.Replace(Resources.Cast_Skill, "t");        // Cast_Skill[0]
            text = text.Replace("【", "[");
            text = text.Replace("】", "]");
            text = text.Replace("！", "!");
            text = text.Replace("１", "1");
            text = text.Replace("２", "2");
            text = text.Replace("３", "3");
            text = text.Replace("４", "4");
            text = text.Replace("５", "5");
            text = text.Replace("６", "6");
            text = text.Replace("７", "7");
            text = text.Replace("８", "8");
            text = text.Replace("９", "9");
            text = text.Replace("０", "0");
            text = text.Replace("。", ".");
            text = text.Replace("（", "(");
            text = text.Replace("）", ")");
            text = text.Replace("[", "");
            text = text.Replace("]", "");
            text = text.Replace("，", ",");

            if (comboDecimal.SelectedIndex == 1)
                text = text.Replace(",", ".");

            if (!AIPolicy.IsMatch(text))
                MessageBox.Show(Resources.ExpressionNoMatch);
            else
            {
                for (var i = 0; i < text.Length; i++)
                {
                    var expr = text.Substring(i, 1);

                    if (expr == "(" || expr == ")" || expr == "." || AIPolicy.IsNumber(expr) || AIPolicy.IDOper(expr) >= 0)
                        continue;

                    MessageBox.Show(Resources.UnableResolveSymbol + expr + Resources.SingleQuote);
                    return;
                }

                AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Condition = GetCondition(text);

                AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Condition =
                    FixCondition(AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Condition);

                txtCondition.Clear();
                txtCondition.Text = ConditionExpression(AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Condition);
            }

        }

        private void BtnAddCtrlClick(object sender, EventArgs e)
        {
            if (AI == null)
            {
                MessageBox.Show(Resources.OpenAIPolicyFirst + Resources.AddAController);
                return;
            }

            if (AI != null && txtCtrlID.Text != "")
            {
                var acCount = AI.ActionControllerCount;

                if (!AIPolicy.IsNumber(txtCtrlID.Text))
                {
                    MessageBox.Show(Resources.NoCtrlIDEntered);
                    return;
                }

                var ctrlID = Convert.ToInt32(txtCtrlID.Text);

                for (var i = 0; i < acCount; i++)
                {
                    if (ctrlID != AI.ActionController[i].ID || ctrlID <= 0) continue;

                    MessageBox.Show(Resources.ControllerIDExists);
                    return;
                }

                acCount++;
                var aIPolicy = new AIPolicy();

                aIPolicy.Signature = 0;
                aIPolicy.ActionControllerCount = acCount;
                aIPolicy.ActionController = new ActionController[acCount];

                for (var j = 0; j < aIPolicy.ActionController.Length - 1; j++)
                {
                    aIPolicy.ActionController[j] = AI.ActionController[j];
                    aIPolicy.ActionController[j].Signature = AI.ActionController[j].Signature;
                    aIPolicy.ActionController[j].ID = AI.ActionController[j].ID;
                    aIPolicy.ActionController[j].ActionSetsCount = AI.ActionController[j].ActionSetsCount;
                    aIPolicy.ActionController[j].ActionSet = AI.ActionController[j].ActionSet;
                }

                AI = aIPolicy;

                AI.ActionController[acCount - 1] = new ActionController();
                AI.ActionController[acCount - 1].Signature = 0;
                AI.ActionController[acCount - 1].ActionSetsCount = 0;

                AI.ActionController[acCount - 1].ActionSet = new ActionSet[aIPolicy.ActionController[acCount - 1].ActionSetsCount];
                AI.ActionController[acCount - 1].ID = ctrlID;
                listController.Items.Clear();

                for (var k = 0; k < AI.ActionControllerCount; k++)
                {
                    var iD = AI.ActionController[k].ID;
                    listController.Items.Add(iD.ToString(CultureInfo.InvariantCulture));
                }

                listActionSet.Items.Clear();
                txtCondition.Clear();
                listProcedure.Items.Clear();
            }

            MessageBox.Show(Resources.ControllerWithID + txtCtrlID.Text + Resources.AddedEndTable);
        }

        private void BtnDelCtrlClick(object sender, EventArgs e)
        {
            if (AI == null)
            {
                MessageBox.Show(Resources.OpenAIPolicyFirst + Resources.DeleteAController);
                return;
            }

            if (AI != null && txtCtrlID.Text != "")
            {
                var actionControllerCount = AI.ActionControllerCount;
                var ctrlID = -1;

                if (!AIPolicy.IsNumber(txtCtrlID.Text))
                {
                    MessageBox.Show(Resources.NoCtrlIDEntered);
                    return;
                }

                var delCtrlID = Convert.ToInt32(txtCtrlID.Text);

                for (var i = 0; i < actionControllerCount; i++)
                    if (delCtrlID == AI.ActionController[i].ID) ctrlID = i;

                if (ctrlID < 0)
                {
                    MessageBox.Show(Resources.ControllerIDNoExists);
                    return;
                }

                var aIPolicy = new AIPolicy();
                aIPolicy.Signature = 0;
                aIPolicy.ActionControllerCount = actionControllerCount - 1;

                var aIPolicy2 = aIPolicy;
                aIPolicy2.ActionController = new ActionController[aIPolicy2.ActionControllerCount];

                for (var j = 0; j < aIPolicy.ActionController.Length; j++)
                {
                    if (j < ctrlID) aIPolicy.ActionController[j] = AI.ActionController[j];
                    if (j >= ctrlID) aIPolicy.ActionController[j] = AI.ActionController[j + 1];
                }

                AI = aIPolicy;
                listController.Items.Clear();

                for (var k = 0; k < AI.ActionControllerCount; k++)
                    listController.Items.Add(AI.ActionController[k].ID.ToString(CultureInfo.InvariantCulture));

                listActionSet.Items.Clear();
                txtCondition.Clear();
                listProcedure.Items.Clear();
            }

            MessageBox.Show(Resources.ControllerWithID + txtCtrlID.Text + Resources.BeenDeleted);
        }

        private void BtnAddActionClick(object sender, EventArgs e)
        {
            var actionID = 0;

            if (AI == null)
            {
                MessageBox.Show(Resources.OpenAIPolicyFirst + Resources.AddAnActionSet);
                return;
            }

            if (listController.SelectedIndex == -1) return;

            if (txtActionName.Text == "")
            {
                MessageBox.Show(Resources.EnterNameForActionSet);
                return;
            }

            var cSelectedIndex = listController.SelectedIndex;
            var asSelectedIndex = listActionSet.SelectedIndex;
            var actionSetCount = AI.ActionController[cSelectedIndex].ActionSetsCount;

            if (AIPolicy.IsNumber(txtActionID.Text))
            {
                actionID = Convert.ToInt32(txtActionID.Text);

                for (var i = 0; i < actionSetCount; i++)
                {
                    if (actionID != AI.ActionController[cSelectedIndex].ActionSet[i].ID) continue;

                    MessageBox.Show(Resources.TheActionSetID + actionID + Resources.AlreadyExists);
                    return;
                }
            }

            if (asSelectedIndex == -1) asSelectedIndex = actionSetCount;

            var actionSet = new ActionSet[actionSetCount + 1];

            for (var j = 0; j <= actionSetCount; j++)
            {
                if (j < asSelectedIndex) actionSet[j] = AI.ActionController[cSelectedIndex].ActionSet[j];
                if (j > asSelectedIndex) actionSet[j] = AI.ActionController[cSelectedIndex].ActionSet[j - 1];
            }

            actionSet[asSelectedIndex] = new ActionSet();
            actionSet[asSelectedIndex].Version = 11;

            if (txtActionID.Text == "")
            {
                var asCount = actionSetCount;
                for (var k = 0; k < actionSetCount; k++)
                    if (asCount < AI.ActionController[cSelectedIndex].ActionSet[k].ID)
                        asCount = AI.ActionController[cSelectedIndex].ActionSet[k].ID;

                for (var x = 0; x <= asCount; x++)
                {
                    for (var y = 0; y < actionSetCount; y++)
                        if (x == AI.ActionController[cSelectedIndex].ActionSet[y].ID)
                            actionSet[asSelectedIndex].ID = -1;

                    if (actionSet[asSelectedIndex].ID != -1)
                    {
                        actionSet[asSelectedIndex].ID = x;
                        break;
                    }

                    actionSet[asSelectedIndex].ID = x;
                }
            }
            else
                actionSet[asSelectedIndex].ID = actionID;

            var tmpFlags = new byte[3];
            tmpFlags[0] = 1;
            var flags = tmpFlags;

            actionSet[asSelectedIndex].Flags = flags;
            actionSet[asSelectedIndex].Name = txtActionName.Text;
            actionSet[asSelectedIndex].Condition = new Condition();
            actionSet[asSelectedIndex].Condition.OperID = 3;
            actionSet[asSelectedIndex].Condition.ArgBytes = 4;

            var value = new byte[] { 0, 0, 128, 63 };

            actionSet[asSelectedIndex].Condition.Value = value;
            actionSet[asSelectedIndex].Condition.SubNodeL = 0;
            actionSet[asSelectedIndex].Condition.SubNodeR = 0;
            actionSet[asSelectedIndex].Condition.ConditionType = 3;
            actionSet[asSelectedIndex].ProcedureCount = 1;

            actionSet[asSelectedIndex].Procedure = new Procedure[1];
            actionSet[asSelectedIndex].Procedure[0] = new Procedure();
            actionSet[asSelectedIndex].Procedure[0].Type = 2;

            var unicode = Encoding.Unicode;
            var rageMsg = unicode.GetBytes("RaGEZONE Forums - http://Forum.RaGEZONE.com");
            var msgSize = rageMsg.Length + 2;
            var mess = new byte[msgSize];

            Array.Copy(rageMsg, mess, rageMsg.Length);
            mess[rageMsg.Length] = 0;
            mess[rageMsg.Length + 1] = 0;

            var parameter = new object[] { msgSize, mess };

            actionSet[asSelectedIndex].Procedure[0].Parameter = parameter;
            actionSet[asSelectedIndex].Procedure[0].Target = 0;
            AI.ActionController[cSelectedIndex].ActionSetsCount = AI.ActionController[cSelectedIndex].ActionSetsCount + 1;
            AI.ActionController[cSelectedIndex].ActionSet = actionSet;

            listActionSet.Items.Clear();

            foreach (var t in AI.ActionController[cSelectedIndex].ActionSet)
                listActionSet.Items.Add("[" + t.ID.ToString(CultureInfo.InvariantCulture) + "] " + t.Name);

            txtCondition.Clear();
            listProcedure.Items.Clear();
        }

        private void BtnDelActionClick(object sender, EventArgs e)
        {
            if (AI == null)
            {
                MessageBox.Show(Resources.OpenAIPolicyFirst + Resources.DeleteAnActionSet);
                return;
            }

            if (listController.SelectedIndex == -1) return;

            var cSelectedIndex = listController.SelectedIndex;
            var asSelectedIndex = listActionSet.SelectedIndex;
            var actionSetCount = AI.ActionController[cSelectedIndex].ActionSetsCount;

            if (asSelectedIndex == -1)
            {
                MessageBox.Show(Resources.NoActionSetSelectedDelete);
                return;
            }

            var actionSet = new ActionSet[actionSetCount - 1];

            for (var i = 0; i < actionSetCount - 1; i++)
            {
                if (i < asSelectedIndex) actionSet[i] = AI.ActionController[cSelectedIndex].ActionSet[i];
                if (i >= asSelectedIndex) actionSet[i] = AI.ActionController[cSelectedIndex].ActionSet[i + 1];
            }

            AI.ActionController[cSelectedIndex].ActionSetsCount = AI.ActionController[cSelectedIndex].ActionSetsCount - 1;
            AI.ActionController[cSelectedIndex].ActionSet = actionSet;

            listActionSet.Items.Clear();

            foreach (var t in AI.ActionController[cSelectedIndex].ActionSet)
                listActionSet.Items.Add("[" + t.ID.ToString(CultureInfo.InvariantCulture) + "] " + t.Name);

            txtCondition.Clear();
            listProcedure.Items.Clear();
        }

        private void BtnEditActionClick(object sender, EventArgs e)
        {
            if (AI == null)
            {
                MessageBox.Show(Resources.OpenAIPolicyFirst + Resources.EditAnActionSet);
                return;
            }

            var cSelectedIndex = listController.SelectedIndex;
            var asSelectedIndex = listActionSet.SelectedIndex;

            if (listController.SelectedIndex != -1 && listActionSet.SelectedIndex != -1)
            {
                if (txtActionName.Text == "")
                {
                    MessageBox.Show(Resources.EnterNewActionSetName);
                    return;
                }

                AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Flags[0] = Convert.ToByte(txtFlag1.Text);
                AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Flags[1] = Convert.ToByte(txtFlag2.Text);
                AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Flags[2] = Convert.ToByte(txtFlag3.Text);
                AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Name = txtActionName.Text;
            }

            listActionSet.Items.Clear();

            foreach (var t in AI.ActionController[cSelectedIndex].ActionSet)
                listActionSet.Items.Add("[" + t.ID.ToString(CultureInfo.InvariantCulture) + "] " + t.Name);

            txtCondition.Clear();
            listProcedure.Items.Clear();
        }

        private void BtnAddProcClick(object sender, EventArgs e)
        {
            if (AI == null)
            {
                MessageBox.Show(Resources.OpenAIPolicyFirst + Resources.AddAProcedure);
                return;
            }

            var cSelectedIndex = listController.SelectedIndex;
            var asSelectedIndex = listActionSet.SelectedIndex;

            if (comboProcedure.SelectedIndex != -1 && asSelectedIndex != -1)
            {
                var pArray = GetParameters(comboProcedure.SelectedIndex,
                    AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Version);

                if (pArray == null) return;

                var pSelectedIndex = listProcedure.SelectedIndex;
                var procedure = AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure;

                AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].ProcedureCount =
                    AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].ProcedureCount + 1;

                var pCount = AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].ProcedureCount;
                AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure = new Procedure[pCount];

                pCount--;

                if (pSelectedIndex == -1) pSelectedIndex = pCount;

                for (var i = 0; i <= pCount; i++)
                {
                    AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i] = new Procedure();

                    if (i < pSelectedIndex)
                    {
                        AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i].Type = procedure[i].Type;
                        AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i].Parameter = procedure[i].Parameter;
                        AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i].Target = procedure[i].Target;
                    }

                    if (i <= pSelectedIndex) continue;

                    AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i].Type = procedure[i - 1].Type;
                    AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i].Parameter = procedure[i - 1].Parameter;
                    AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i].Target = procedure[i - 1].Target;
                }

                AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Type = comboProcedure.SelectedIndex;
                AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Parameter = pArray;

                AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[pSelectedIndex].Target =
                    Convert.ToInt32(ProcedureTarget(txtParamTarget.Text));

                listProcedure.Items.Clear();

                for (var j = 0; j < AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure.Length; j++)
                    listProcedure.Items.Add("[" + j.ToString(CultureInfo.InvariantCulture) + "] " +
                        ProcedureExpression(AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[j],
                        AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Version));
            }
            comboProcedure.SelectedIndex = -1;
        }

        private void BtnDelProcClick(object sender, EventArgs e)
        {
            if (AI == null)
            {
                MessageBox.Show(Resources.OpenAIPolicyFirst + Resources.DeleteAProcedure);
                return;
            }

            if (listProcedure.SelectedIndex != -1)
            {
                var cSelectedIndex = listController.SelectedIndex;
                var asSelectedIndex = listActionSet.SelectedIndex;
                var pSelectedIndex = listProcedure.SelectedIndex;

                if (AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure.Length <= 1)
                {
                    MessageBox.Show(Resources.RetainProcedure);
                    return;
                }

                var procedures = AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure;
                var pCount = AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].ProcedureCount - 1;

                AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure = new Procedure[pCount];

                for (var i = 0; i < pCount; i++)
                {
                    AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i] = new Procedure();
                    if (i < pSelectedIndex)
                    {
                        AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i].Type = procedures[i].Type;
                        AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i].Parameter = procedures[i].Parameter;
                        AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i].Target = procedures[i].Target;
                    }

                    if (i < pSelectedIndex) continue;

                    AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i].Type = procedures[i + 1].Type;
                    AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i].Parameter = procedures[i + 1].Parameter;
                    AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[i].Target = procedures[i + 1].Target;
                }

                AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].ProcedureCount =
                    AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].ProcedureCount - 1;

                listProcedure.Items.Clear();

                for (var j = 0; j < AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure.Length; j++)
                    listProcedure.Items.Add("[" + j.ToString(CultureInfo.InvariantCulture) + "] " +
                        ProcedureExpression(AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Procedure[j],
                        AI.ActionController[cSelectedIndex].ActionSet[asSelectedIndex].Version));
            }

            comboProcedure.SelectedIndex = -1;
        }

        private void BtnEditProcClick(object sender, EventArgs e)
        {
            if (AI == null)
            {
                MessageBox.Show(Resources.OpenAIPolicyFirst + Resources.EditAProcedure);
                return;
            }

            if (listProcedure.SelectedIndex == -1) return;

            var cSelectedIndex = listController.SelectedIndex;
            var asSselectedIndex = listActionSet.SelectedIndex;
            var pSelectedIndex = listProcedure.SelectedIndex;

            AI.ActionController[cSelectedIndex].ActionSet[asSselectedIndex].Procedure[pSelectedIndex].Parameter =
                GetParameters(AI.ActionController[cSelectedIndex].ActionSet[asSselectedIndex].Procedure[pSelectedIndex].Type,
                AI.ActionController[cSelectedIndex].ActionSet[asSselectedIndex].Version);

            listProcedure.Items.Clear();

            for (var i = 0; i < AI.ActionController[cSelectedIndex].ActionSet[asSselectedIndex].Procedure.Length; i++)
            {
                var num = i;

                listProcedure.Items.Add("[" + num.ToString(CultureInfo.InvariantCulture) + "] " +
                    ProcedureExpression(AI.ActionController[cSelectedIndex].ActionSet[asSselectedIndex].Procedure[i],
                    AI.ActionController[cSelectedIndex].ActionSet[asSselectedIndex].Version));
            }
        }

        private void BtnAISearchClick(object sender, EventArgs e)
        {
            if (AI == null)
            {
                MessageBox.Show(Resources.OpenAIPolicyFirst + Resources.SearchAFile);
                return;
            }

            if (AI != null && comboCat.SelectedIndex > -1)
            {
                var i = 0;
                var j = 0;
                var k = 0;

                if (listController.SelectedIndex > -1) i = listController.SelectedIndex;

                if (listActionSet.SelectedIndex > -1)
                {
                    j = listActionSet.SelectedIndex;

                    if (comboCat.SelectedItem.ToString() == "Conditions") j++;
                }

                if (listProcedure.SelectedIndex > -1)
                {
                    k = listProcedure.SelectedIndex;
                    if (comboCat.SelectedItem.ToString() == "Procedures") k++;
                }

                while (i < AI.ActionController.Length)
                {
                    if (comboCat.SelectedItem.ToString() == "AI Control Link" &&
                        comboSubCat.SelectedIndex > -1 && txtPattern.Text != "")
                    {
                        var num = 0;

                        if (comboSubCat.SelectedItem.ToString() == "Task")
                        {
                            MessageBox.Show(Resources.TaskAINotFound);
                            return;
                        }

                        if (comboSubCat.SelectedItem.ToString() == "Element NPC")
                            num = Convert.ToInt32(txtPattern.Text);

                        if (AI.ActionController[i].ID == num)
                        {
                            listController.SelectedIndex = i;
                            return;
                        }
                    }

                    while (j < AI.ActionController[i].ActionSet.Length)
                    {
                        if (comboCat.SelectedItem.ToString() == "Conditions" && txtPattern.Text != "" &&
                                ConditionExpression(AI.ActionController[i].ActionSet[j].Condition).Contains(txtPattern.Text))
                        {
                            listController.SelectedIndex = i;
                            listActionSet.SelectedIndex = j;
                            return;
                        }

                        if (comboCat.SelectedItem.ToString() == "Procedures" && comboSubCat.SelectedIndex > -1)
                        {
                            while (k < AI.ActionController[i].ActionSet[j].Procedure.Length)
                            {
                                if (AI.ActionController[i].ActionSet[j].Procedure[k].Type == comboSubCat.SelectedIndex &&
                                        (txtPattern.Text == "" || ProcedureExpression(AI.ActionController[i].ActionSet[j].Procedure[k],
                                        AI.ActionController[i].ActionSet[j].Version).Contains(txtPattern.Text)))
                                {
                                    listController.SelectedIndex = i;
                                    listActionSet.SelectedIndex = j;
                                    listProcedure.SelectedIndex = k;
                                    return;
                                }
                                k++;
                            }
                            k = 0;
                        }

                        if (comboCat.SelectedItem.ToString() == "Targets" && comboSubCat.SelectedIndex > -1)
                        {
                            while (k < AI.ActionController[i].ActionSet[j].Procedure.Length)
                            {
                                if (AI.ActionController[i].ActionSet[j].Procedure[k].Target == comboSubCat.SelectedIndex &&
                                        (txtPattern.Text == "" || ProcedureExpression(AI.ActionController[i].ActionSet[j].Procedure[k],
                                        AI.ActionController[i].ActionSet[j].Version).Contains(txtPattern.Text)))
                                {
                                    listController.SelectedIndex = i;
                                    listActionSet.SelectedIndex = j;
                                    listProcedure.SelectedIndex = k;
                                    return;
                                }
                                k++;
                            }
                            k = 0;
                        }
                        j++;
                    }
                    j = 0;
                    i++;
                }
            }
            MessageBox.Show(Resources.SearchNoResults);
        }

        private void UpdateComboCat(object sender, EventArgs e)
        {
            if (comboCat.SelectedIndex <= -1) return;

            comboSubCat.Items.Clear();

            // Conditions
            if (comboCat.SelectedIndex == 1)
            {
                comboSubCat.Items.Add("All");
                comboSubCat.SelectedIndex = 0;
                return;
            }

            // AI Control Link
            if (comboCat.SelectedIndex != 0) return;

            var aiControlLink = new object[]
            {
                "Task",
                "Element NPC"
            };

            comboSubCat.Items.AddRange(aiControlLink);
            comboSubCat.SelectedIndex = 0;

            // Procedures
            if (comboCat.SelectedIndex == 2)
            {
                var procs = new object[]
                {
                    "Attack",
                    "Cast_Skill",
                    "Broadcast_Message",
                    "Reset_Aggro",
                    "Execute_ActionSet",
                    "Disable_ActionSet",
                    "Enable_ActionSet",
                    "Create_Timer",
                    "Remove_Timer",
                    "Flee",
                    "Be_Taunted",
                    "Fade_Target",
                    "Fade_Aggro",
                    "Break",
                    "NPC_Generator",
                    "Summon_Mob",
                    "Unknown16",
                    "Set_Path",
                    "Disappear",
                    "N/A",
                    "N/A",
                    "Respawn",
                    "Set_Value",
                    "Add_Value",
                    "N/A",
                    "Set_Mob_Attribute",
                    "Drop_WarSoul"
                };

                comboSubCat.Items.AddRange(procs);
                comboSubCat.SelectedIndex = 0;
            }
            else
                return;

            // Targets
            if (comboCat.SelectedIndex != 3) return;

            var trgt = new object[]
            {
                "AGGRO_FIRST",
                "AGGRO_SECOND",
                "AGGRO_SECOND_RAND",
                "MOST_HP",
                "MOST_MP",
                "LEAST_HP",
                "CLASS_COMBO",
                "SELF"
            };

            comboSubCat.Items.AddRange(trgt);
            comboSubCat.SelectedIndex = 0;
        }

        private void UpdateComboProc(object sender, EventArgs e)
        {
            ClearParams();

            if (AI == null || listController.SelectedIndex <= -1 ||
                listActionSet.SelectedIndex <= -1) return;

            ProcedureList();
        }

    }
}
